//@author A0105536L
package Logic;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import Object.DSException;
import Task.DSTask;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.antlr.runtime.tree.Tree;

import com.joestelmach.natty.DateGroup;
import com.joestelmach.natty.Parser;

import java.util.List;

/**
 * Class DSTaskFactory. This class is to create a new task and to create a
 * update task
 * 
 * 
 */
public class DSTaskFactory {

	static final int NO_MODE = -1;
	static final int TASK_ID_MODE = 1;
	static final int TASK_NAME_MODE = 2;
	static final int TASK_START_DATE_MODE = 3;
	static final int TASK_END_DATE_MODE = 4;
	static final int TASK_START_TIME_MODE = 5;
	static final int TASK_END_TIME_MODE = 6;

	/**
	 * This method is to create a new task
	 * 
	 * @param input
	 *            A string that contains what to add
	 * @return Return a new DSTask
	 * @throws DSException
	 */
	public static DSTask createNewTask(String input) throws DSException {
		return analyseAddInput(input);
	}

	public static DSTask convertToTask(String input) throws DSException {
		return analyseAddInput(input);
	}

	/**
	 * This method is to analyze the command input
	 * 
	 * @param input
	 *            A string that contains information that to be added
	 * @return Return a new DSTask
	 * @throws DSException
	 */
	@SuppressWarnings("deprecation")
	private static DSTask analyseAddInput(String input) throws DSException {

		try {
			int by_num = countBy(input);
			int from_num = countFrom(input);
			int tag_num;
			if (!input.endsWith("tag")) {
				tag_num = countTag(input);
			}  else {
				tag_num = 0;
			}
			int by = 1, from = 1, tag = 1;
			boolean from_and_to = has_From_and_To(input);
			DSTask newTask;

			newTask = new DSTask();
			String[] taskDetails = splitCommandStrings(input);

			int currentMode = NO_MODE;
			String taskName = "", taskID = "";
			int taskStartTime = -1, taskEndTime = -1;
			Date taskStartDate = null, taskEndDate = null;
			Parser nattyParser = new Parser();
			for (int i = 0; i < taskDetails.length; i++) {
				taskDetails[i].trim();
				if (!taskDetails[i].equalsIgnoreCase("from")
						&& !taskDetails[i].equalsIgnoreCase("by")
						&& currentMode != TASK_NAME_MODE) {
					if(taskName.equals("")){
						taskName+=taskDetails[i];
						} else {
							taskName += " "+taskDetails[i];
						}
					
					currentMode = TASK_NAME_MODE;

				} else if (taskDetails[i].equalsIgnoreCase("by")) {
					// xxx by deadline
					if (by == by_num) {
						i++;
						String parseNatty = "";
						while (i < taskDetails.length) {
							if (taskDetails[i].equalsIgnoreCase("tag")) {
								i--;
								break;
							}
							parseNatty = parseNatty + " " + taskDetails[i];
							i++;
						}
						List<DateGroup> enddate = new ArrayList<DateGroup>();
						enddate = nattyParser.parse(parseNatty);
						if (!enddate.isEmpty()) {
							taskEndDate = (enddate).get(0).getDates().get(0);

							if (!enddate.get(0).isTimeInferred()) {
								String format = "%02d";
								taskEndTime = Integer.parseInt(String.format(
										format, taskEndDate.getHours())
										+ String.format(format,
												taskEndDate.getMinutes()));
							}
						} else {
							taskName = taskName + " by" + " " + parseNatty;
						}
					} else {
						by++;
						taskName = taskName + " " + taskDetails[i];
					}

				} else if (taskDetails[i].equalsIgnoreCase("from")) {
					// from start date to end date
					// after "from"
					if (from_and_to && from == from_num) {
						i++;
						String startString = "", endString = "";
						while (!taskDetails[i].equalsIgnoreCase("to")) {
							startString = startString + " " + taskDetails[i];
							i++;
						}
						List<DateGroup> start = new ArrayList<DateGroup>();
						start = nattyParser.parse(startString);
						// after "to"
						i++;
						while (i < taskDetails.length) {
							if (taskDetails[i].equalsIgnoreCase("tag")) {
								i--;
								break;
							}
							endString = endString + " " + taskDetails[i];
							i++;
						}
						List<DateGroup> end = new ArrayList<DateGroup>();
						end = nattyParser.parse(endString);
						if (!start.isEmpty() && !end.isEmpty()) {
							taskStartDate = start.get(0).getDates().get(0);
							if (!start.get(0).isTimeInferred()) {
								String format = "%02d";
								taskStartTime = Integer.parseInt(String.format(
										format, taskStartDate.getHours())
										+ String.format(format,
												taskStartDate.getMinutes()));
							}

							taskEndDate = end.get(0).getDates().get(0);
							if (!end.get(0).isTimeInferred()) {
								String format = "%02d";
								taskEndTime = Integer.parseInt(String.format(
										format, taskEndDate.getHours())
										+ String.format(format,
												taskEndDate.getMinutes()));
							}
						} else {
							taskName = taskName + " from" + " " + startString
									+ " to" + " " + endString;
						}
					} else {
						from++;
						taskName = taskName + " " + taskDetails[i];
					}
				} else if (taskDetails[i].equalsIgnoreCase("tag")) {
					if (tag == tag_num) {
						i++;
						while (i < taskDetails.length) {
							if(taskDetails[i].length()>30){
								taskDetails[i] = taskDetails[i].substring(0,30);
							}
							if (!taskDetails[i].equals(""))
								newTask.addTag(taskDetails[i]);

							i++;
						}
					} else {
						tag++;
						taskName = taskName + " " + taskDetails[i];
					}
				}

				else {

					switch (currentMode) {

					case (TASK_NAME_MODE):

						taskName = taskName + " " + taskDetails[i];
					}
				}
			}
			if (taskName != "") {
				newTask.setTaskName(taskName);
			} else {
				newTask.setTaskName("No Task");
			}
			if (taskEndDate != null) {
				newTask.setTaskEndDate(taskEndDate);
			}
			if (taskEndTime != -1) {
				newTask.setTaskEndTime(taskEndTime);
			}
			if (taskStartTime != -1) {
				newTask.setTaskStartTime(taskStartTime);
			}
			if (taskStartDate != null) {
				newTask.setTaskStartDate(taskStartDate);
			}
			if (taskID != "") {
				newTask.setTaskId(taskID);
			}

			return newTask;

		} catch (ParseException e) {
			throw new DSException("");
		}
	}

	/**
	 * This method is to create a update task
	 * 
	 * @param input
	 *            String that contains information to be updated
	 * @param taskToUpd
	 *            DSTask that to be updated
	 * @return Return a DSTask
	 */
	public static DSTask updateTask(String input, DSTask taskToUpd) {
		return analyseUpdateInput(input, taskToUpd);
	}

	/**
	 * This method is to analyze update input
	 * 
	 * @param input
	 *            String that contains information to be updated
	 * @param taskToUpd
	 *            DSTask that to be updated
	 * @return Return a DSTask
	 */
	private static DSTask analyseUpdateInput(String input, DSTask tasktoupd) {

		int by_num1 = countBy(input);
		int from_num1 = countFrom(input);
		int tag_num1;
		if (!input.endsWith("tag")&&!input.startsWith("tag")) {
			
			tag_num1 = countTag(input);
		}  else if(input.startsWith("tag")){
			tag_num1 = countTag(input);
			
		} else {
			
			tag_num1 = 1;
		}
		int by1 = 1, from1 = 1, tag1 = 1;
		boolean from_and_to1 = has_From_and_To(input);

		String[] taskDetails = splitCommandStrings(input);
		Parser nattyParser = new Parser();
		int currentMode = NO_MODE;
		String taskName = "", taskID = "";
		int taskStartTime = -1, taskEndTime = -1;
		Date taskStartDate = null, taskEndDate = null;
		List<DateGroup> enddate = new ArrayList<DateGroup>();
		List<DateGroup> startdate = new ArrayList<DateGroup>();
		for (int i = 0; i < taskDetails.length; i++) {
			taskDetails[i].trim();
			if (!taskDetails[i].equalsIgnoreCase("from")
					&& !taskDetails[i].equalsIgnoreCase("by")
					&& currentMode != TASK_NAME_MODE
					&& !taskDetails[i].equals("tag")) {

				if(taskName.equals("")){
					taskName+=taskDetails[i];
					} else {
						taskName += " "+taskDetails[i];
					}
				currentMode = TASK_NAME_MODE;

			} else if (taskDetails[i].equalsIgnoreCase("by")) {
				// xxx by deadline
				if (by1 == by_num1) {
					i++;
					String parseNatty = "";
					while (i < taskDetails.length) {
						if (taskDetails[i].equalsIgnoreCase("tag")) {
							i--;
							break;
						}
						parseNatty = parseNatty + " " + taskDetails[i];
						i++;
					}

					enddate = nattyParser.parse(parseNatty);
					if (!enddate.isEmpty()) {
						taskEndDate = (enddate).get(0).getDates().get(0);
						if (!enddate.get(0).isTimeInferred()) {
							String format = "%02d";
							taskEndTime = Integer.parseInt(String.format(
									format, taskEndDate.getHours())
									+ String.format(format,
											taskEndDate.getMinutes()));
						}
					} else {
						taskName = taskName + " by" + parseNatty;
					}
				} else {
					by1++;
					taskName = taskName + " " + taskDetails[i];
				}
			} else if (taskDetails[i].equalsIgnoreCase("from")) {
				// from start date to end date
				// after "from"
				if (from_and_to1 && from1 == from_num1) {
					i++;
					String startString = "", endString = "";
					while (!taskDetails[i].equalsIgnoreCase("to")) {
						startString = startString + " " + taskDetails[i];
						i++;
					}
					startdate = nattyParser.parse(startString);

					// after "to"
					i++;
					while (i < taskDetails.length) {
						if (taskDetails[i].equalsIgnoreCase("tag")) {
							i--;
							break;
						}
						endString = endString + " " + taskDetails[i];
						i++;
					}

					enddate = nattyParser.parse(endString);
					if (!startdate.isEmpty() && !enddate.isEmpty()) {

						taskStartDate = startdate.get(0).getDates().get(0);
						if (!startdate.get(0).isTimeInferred()) {
							String format = "%02d";
							taskStartTime = Integer.parseInt(String.format(
									format, taskStartDate.getHours())
									+ String.format(format,
											taskStartDate.getMinutes()));
						}

						taskEndDate = enddate.get(0).getDates().get(0);
						if (!enddate.get(0).isTimeInferred()) {
							String format = "%02d";
							taskEndTime = Integer.parseInt(String.format(
									format, taskEndDate.getHours())
									+ String.format(format,
											taskEndDate.getMinutes()));
						}
					} else {
						taskName = taskName + " from" + startString + " to"
								+ endString;
					}
				} else {
					from1++;
					taskName = taskName + " " + taskDetails[i];
				}
			} else if (taskDetails[i].equalsIgnoreCase("tag")) {
				if (tag1 == tag_num1) {
					
					if (i == taskDetails.length-1) {
					System.out.println("ho");
						break;
					
					}

					i++;
					while (i < taskDetails.length) {
						if (!taskDetails[i].equals(""))
							tasktoupd.addTag(taskDetails[i]);

						i++;
					}
				} else {
					System.out.println("ff");
					tag1++;
					taskName = taskName + " " + taskDetails[i];
				}
			} else {

				switch (currentMode) {
				case (TASK_ID_MODE):
					taskID += taskDetails[i];
					break;

				case (TASK_NAME_MODE):

					taskName = taskName + " " + taskDetails[i];

					break;

				}
			}
		}
		if (taskName != "") {
			
			tasktoupd.setTaskName(taskName);
			
		}
		if (taskEndDate != null) {
			tasktoupd.setTaskEndDate(taskEndDate);
		}
		if (taskEndTime != -1) {
			tasktoupd.setTaskEndTime(taskEndTime);
		}
		if (taskStartTime != -1) {
			tasktoupd.setTaskStartTime(taskStartTime);
		}
		if (taskStartDate != null) {
			tasktoupd.setTaskStartDate(taskStartDate);
		}
		if (taskID != "") {
			tasktoupd.setTaskId(taskID);
		}

		return tasktoupd;

	}

	/**
	 * This method is to count how many "by" in a string
	 * 
	 * @param str
	 *            A given string
	 * @return Return number of "by" in the string
	 */
	private static int countBy(String str) {
		return str.split("by ").length - 1;
	}

	/**
	 * This method is to count how many "from" in a string
	 * 
	 * @param str
	 *            A given string
	 * @return Return number of "from" in the string
	 */
	private static int countFrom(String str) {
		return str.split("from ").length - 1;
	}

	/**
	 * This method is to count how many "tag" in a string
	 * 
	 * @param str
	 *            A given string
	 * @return Return number of "tag" in the string
	 */
	private static int countTag(String str) {
		return str.split("tag ").length - 1;
	}

	private static String[] splitCommandStrings(String userCommand) {
		return userCommand.trim().split(" ");
	}

	private static boolean has_From_and_To(String str) {
		return str.contains("from ") && str.contains("to ");
	}

}